//////////////////////////////////////////////////////////////////////
// FSM.h
//
// SHEN Fangyang
// me@shenfy.com
//
// Copyright (C) SHEN Fangyang, 2011, All rights reserved.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
//  Brief Description
//
//	Simple finite state machine
//
//////////////////////////////////////////////////////////////////////
#pragma once
#include <string>
#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include <boost/function.hpp>

namespace grflib
{
	namespace engine
	{
		namespace AI
		{
			class CFSM;
			class CFSMState;
			struct CMsg;

			typedef boost::unordered_map<std::string /*state id*/, boost::shared_ptr<CFSMState>> StateMap;
			typedef boost::unordered_map<std::string /*msg id*/, std::string /*output state*/> TransitionTable;
			typedef boost::unordered_map<std::string /*msg id*/, boost::function<void (const CMsg &)>> ByproductTable;

			class CFSMState
			{
			public:
				CFSMState(void);
				~CFSMState(void);

				//use boost::bind to set entry and exit behaviors
				//USAGE:
				//	class A { void f(int i); };
				//	shared_ptr<A> pA(new A);
				//	bind(&A::f, pA, _1);
				boost::function<void (const CMsg &)> m_onEnterFunc;
				boost::function<void (const CMsg &)> m_onExitFunc;

				//manage transitions
				void AddTransition(std::string msgId, std::string destState);
				void AddTransition(std::string msgId, std::string destState,
								   boost::function<void (const CMsg &)> &byproductFunc);
				void Clear(void);

				//trigger transition from this state to another
				bool GetNextState(const CMsg &msg, std::string &outputState);
				ByproductTable &GetByProdTable(void);

				friend class CFSM;

			private:
				void OnEnter(const CMsg &msg);
				void OnExit(const CMsg &msg);
				void Byproduct(const CMsg &msg);

			private:
				TransitionTable m_transTable;
				ByproductTable m_byprodTable;
			};

			class CFSM
			{
			public:
				CFSM(void);
				~CFSM(void);

				//state machine builder
				void AddState(const std::string &id, boost::shared_ptr<CFSMState> &pState);
				void RemoveState(const std::string &id);
				void SetStartState(const std::string &id);

				void AddState(char *szId, boost::shared_ptr<CFSMState> &pState);
				void RemoveState(char *szId);
				void SetStartState(char *szId);

				//member accessor
				boost::shared_ptr<CFSMState> GetCurrentState(void);
				std::string StateToId(CFSMState *pState);

				//action
				bool OnMsg(const CMsg &msg);
				bool ForceCurrentState(const std::string &id); //DANGEROUS! avoid using this

			private:
				StateMap m_stateMap;
				boost::shared_ptr<CFSMState> m_pCurState;
			};
		} //namespace AI
	} //namespace engine
} //namespace grflib